import { backupRootDeleteChild, splitLeftRightRoot, hasChild } from './index'
import { getVerticalXmindSummaryPos, getXmindSummaryPos } from './node'
import setTopicObjectStyle from './style'
import { calculateTopicsSize, getTopicNodeRect } from './size'
import { hierarchy, tree } from 'd3-hierarchy'
import markerShape from '../marker-shape'

let nodes = []
let links = []
const smallSpacing = 10
const largeSpacing = 24

/**
 * 根据根节点root平铺所有节点数据并且赋予节点x/y坐标
 * @param {*} root
 * @param { String } direction 子节点展开方向，默认向右
 * @param { String } skeletonTheme 主题结构
 * @param { Boolean } compact 紧凑型布局
 * @param { Boolean } aligning 统计主题对齐
 */
export function dataTreeLayoutPackage (root, direction, skeletonTheme, compact, aligning) {
  const d3Tree = tree()
  const hierarchydata = d3Tree(hierarchy(root, d => d.children))
  nodes = hierarchydata.descendants()
  setTopicObjectStyle(nodes, direction, skeletonTheme, !!nodes[0].data.pos)
  calculateTopicsSize(nodes, compact, aligning, skeletonTheme)
  firstWalk(nodes, direction, skeletonTheme, compact)
  secondWalk(nodes, direction, compact)
  thirdWalk(nodes, direction)
  setParentNodeCenter(nodes[0], direction, skeletonTheme)
  if (['org.xmind.ui.tree.right', 'org.xmind.ui.timeline.vertical'].includes(skeletonTheme.structure) && nodes[0].data.isRoot) {
    treeResetRootNodePos(nodes)
  }
  links = hierarchydata.links()
  return {
    nodes,
    links
  }
}

/**
 * 不同布局计算外边框文字索占用的高度
 * @param {*} nodes
 * @param {*} direction
 */
export function calcuOutBorderTextRect (node, direction) {
  const outBorder = node.data.outBorder
  if (outBorder?.text) {
    const { minX, maxX } = direction === 'bottom' ? getVerticalXmindSummaryPos(node) : getXmindSummaryPos(node)
    const { width, height } = getTopicNodeRect({
      text: outBorder.text,
      fontSize: 12,
      maxWidth: `${maxX - minX - (direction === 'bottom' ? 0 : 16)}px`,
      padding: '6px 14px',
      fontFamily: 'nevermind'
    })
    outBorder.width = width
    outBorder.height = height
  } else if (outBorder) {
    outBorder.width = 0
    outBorder.height = 0
  }
}

/**
 * 首次遍历节点树动态计算节点的横坐标x和子节点的总高度
 * @param {*} nodes
 * @param { String } direction 子节点展开方向
 * @param { String } skeletonTheme
 * @param { Boolean } compact 紧凑型布局
 */
export function firstWalk (nodes, direction, skeletonTheme, compact) {
  const structure = skeletonTheme.structure
  nodes.forEach(node => {
    node.direction = direction
    node.gap = 0
    // 独立主题单独处理x,y坐标
    if (node.data.pos) {
      node.x = node.data.pos.x
      node.y = node.data.pos.y
    }
    if (node.data.isRoot) {
      if (structure === 'org.xmind.ui.map.clockwise' && node.style.lineStyle.branch !== 'lineBight') {
        node.gap = Math.min(node.width / 2, 70)
      } else if (['org.xmind.ui.tree.right', 'org.xmind.ui.timeline.vertical'].includes(structure)) {
        node.gap = node.width / 2 + 12
      }
    }
    const lineEndJoin = node.parent?.style.lineStyle.lineEndJoin || 'arrow-none'
    const lineWidth = (typeof node.parent?.style.lineStyle.lineWidth) === 'string' ? 1 : (node.parent?.style.lineStyle.lineWidth || 1)
    if (node.parent && direction === 'right') {
      const spacing = compact ? Math.max(20, node.parent.style.spacing * 0.5) : node.parent.style.spacing
      if (['org.xmind.ui.tree.right', 'org.xmind.ui.timeline.vertical'].includes(structure) && node.parent.data.isRoot) {
        node.x = node.parent.x + node.parent.width / 2 + spacing + markerShape[lineEndJoin].markerWidth * lineWidth
      } else {
        node.x = node.parent.x + node.parent.width + spacing + markerShape[lineEndJoin].markerWidth * lineWidth
      }
    }
    if (node.parent && direction === 'left') {
      const spacing = compact ? Math.max(20, node.parent.style.spacing * 0.5) : node.parent.style.spacing
      if (['org.xmind.ui.tree.right', 'org.xmind.ui.timeline.vertical'].includes(structure) && node.parent.data.isRoot) {
        node.x = node.parent.x + node.parent.width / 2 - node.width - spacing - markerShape[lineEndJoin].markerWidth * lineWidth
      } else {
        node.x = node.parent.x - node.width - spacing - markerShape[lineEndJoin].markerWidth * lineWidth
      }
    }
    if (node.parent && direction === 'bottom') {
      const spacing = compact ? Math.max(20, node.parent.style.spacing * 0.5) : node.parent.style.spacing
      node.y = node.parent.y + node.parent.height + spacing + markerShape[lineEndJoin].markerWidth * lineWidth
    }
    if (hasChild(node)) {
      const len = node.children.length
      const initHeight = 0
      node.childrenAreaHeight = node.children.reduce((prev, cur) => {
        return prev + cur[direction === 'bottom' ? 'width' : 'height']
      }, initHeight) + (len - 1) * ((node.depth > 0 ? smallSpacing : largeSpacing) * (compact ? 0.5 : 1))
    } else {
      node.childrenAreaHeight = 0
    }
  })
  // 根据外框节点高度重新计算上下布局的y坐标
  nodes.forEach(node => {
    if (node.parent) {
      calcuOutBorderTextRect(node, direction)
      const lineEndJoin = node.parent?.style.lineStyle.lineEndJoin || 'arrow-none'
      const lineWidth = (typeof node.parent?.style.lineStyle.lineWidth) === 'string' ? 1 : (node.parent?.style.lineStyle.lineWidth || 1)
      if (node.direction !== 'bottom') {
        node.y = node.parent.y + node.parent.height + 36 + markerShape[lineEndJoin].markerWidth * lineWidth + (node.data.outBorder?.height || 0)
      } else {
        node.x = node.parent.x + node.parent.width + 36 + markerShape[lineEndJoin].markerWidth * lineWidth + (node.data.outBorder?.width || 0)
      }
    }
  })
}

/**
 * 左右布局第二次遍历动态计算每个节点的y坐标
 * 上下布局第二次遍历动态计算每个节点的x坐标
 * @param {*} nodes
 * @param { String } direction 子节点展开方向
 * @param { Boolean } compact 紧凑型布局
 */
export function secondWalk (nodes, direction, compact) {
  if (direction !== 'bottom') {
    nodes.forEach(node => {
      if (hasChild(node)) {
        const isunderline = ['underline', 'doubleUnderline'].includes(node.style.shape)
        const firstChild = node.children[0]
        const area = ['underline', 'doubleUnderline'].includes(firstChild.style.shape) ? firstChild.height : 0
        let startY = node.y + node.height / (isunderline ? 1 : 2) - (node.childrenAreaHeight + area) / 2
        node.children.forEach((n) => {
          n.y = startY
          startY += n.height + ((node.depth > 0 ? smallSpacing : largeSpacing) * (compact ? 0.5 : 1))
        })
      }
    })
  } else {
    nodes.forEach(node => {
      if (hasChild(node)) {
        const x = node.x + node.width / 2 - node.childrenAreaHeight / 2
        let startX = x
        node.children.forEach(n => {
          n.x = startX
          startX += n.width + ((node.depth > 0 ? smallSpacing : largeSpacing) * (compact ? 0.5 : 1))
        })
      }
    })
  }
}

/**
 * 第三次遍历判断子节点所占的高度之和是否大于该节点自身高度，若高于自身高度则重新计算子节点y坐标
 * @param {*} nodes
 * @param { String } direction 子节点展开方向
 */
export function thirdWalk (nodes, direction) {
  const underLineShapes = ['underline', 'doubleUnderline']
  if (direction !== 'bottom') {
    nodes.forEach(node => {
      if (hasChild(node)) {
        const firstChild = node.children[0]
        const lastChild = node.children[node.children.length - 1]
        const area = lastChild.y + lastChild.height - (underLineShapes.includes(firstChild.style.shape) ? firstChild.height + firstChild.y : firstChild.y)
        const childShape = firstChild.style.shape
        if (!underLineShapes.includes(node.style.shape)) {
          if (node.children.length <= 1) {
            if (!underLineShapes.includes(childShape)) {
              updateBrothers(node, 0, Math.max((area - node.height) / 2, 0), Math.max((area - node.height) / 2, 0), direction)
            } else {
              updateBrothers(node, Math.max(area / 2 + firstChild.height - node.height / 2, 0), 0, direction)
            }
          } else {
            const firstChild = node.children[0]
            const lastChild = node.children[node.children.length - 1]
            const upOffset = Math.max(node.y - firstChild.y, 0)
            const downOffset = Math.max(0, lastChild.y + lastChild.height - node.y - node.height)
            updateBrothers(node, upOffset, downOffset, direction)
          }
        } else {
          if (node.children.length > 1) {
            updateBrothers(node, Math.max(area / 2 + (underLineShapes.includes(childShape) ? firstChild.height : 0) - node.height, 0), area / 2, direction)
          } else {
            if (!underLineShapes.includes(childShape)) {
              updateBrothers(node, 0, firstChild.height / 2, direction)
            } else {
              updateBrothers(node, Math.max(area / 2 + firstChild.height - node.height, 0), 0, direction)
            }
          }
        }
      }
      if (node.data.outBorder?.height) {
        updateBrothers(node, node.data.outBorder.height, 0, direction)
      }
    })
  } else {
    nodes.forEach(node => {
      const difference = node.childrenAreaHeight - node.width
      if (difference > 0) {
        const upOffset = node.x - node.children[0].x
        const downOffset = Math.max(0, node.children[node.children.length - 1].x + node.children[node.children.length - 1].width - node.x - node.width)
        updateBrothers(node, upOffset, downOffset, direction)
      }
    })
  }
}

/**
 * 更新兄弟节点y坐标
 * @param {*} node
 * @param {*} upOffset
 * @param {*} downOffset
 * @param {*} direction
 */
export function updateBrothers (node, upOffset, downOffset, direction) {
  if (node.parent) {
    const childrenList = node.parent.children
    const index = childrenList.findIndex(item => item === node)
    childrenList.forEach((item, _index) => {
      if (item.data._id === node.data._id) return
      if (_index < index) {
        item[direction !== 'bottom' ? 'y' : 'x'] -= upOffset
        if (hasChild(item)) {
          updateChildren(item.children, direction !== 'bottom' ? 'y' : 'x', -upOffset)
        }
      } else if (_index > index) {
        item[direction !== 'bottom' ? 'y' : 'x'] += downOffset
        if (hasChild(item)) {
          updateChildren(item.children, direction !== 'bottom' ? 'y' : 'x', downOffset)
        }
      }
    })
    updateBrothers(node.parent, upOffset, downOffset, direction)
  }
}

/**
 * 更新节点的所有子节点的位置
 * @param {*} children
 * @param {*} prop
 * @param {*} offset
 */
export function updateChildren (children, prop, offset) {
  children.forEach((item) => {
    item[prop] += offset
    if (hasChild(item)) {
      updateChildren(item.children, prop, offset)
    }
  })
}

/**
 * 父节点的位置相对于子节点居中显示
 * @param {*} node
 * @param {*} direction
 */
export function setParentNodeCenter (node, direction, skeletonTheme) {
  // 独立主题的根节点x,y坐标
  const independentRootX = node.x
  const independentRootY = node.y

  function _c (node, direction) {
    const pos = direction === 'bottom' ? 'x' : 'y'
    const sizeName = direction === 'bottom' ? 'width' : 'height'
    const children = node.children || []
    if (children.length) {
      const firstChild = children[0]
      const lastChild = children[children.length - 1]
      const area = (['underline', 'doubleUnderline'].includes(firstChild.style.shape) && direction !== 'bottom' ? firstChild[sizeName] + firstChild[pos] : firstChild[pos]) + lastChild[pos] + lastChild[sizeName]
      const parentCenter = Number((area / 2).toFixed(5))
      if (!['underline', 'doubleUnderline'].includes(node.style.shape) || direction === 'bottom') {
        if (Number((node[pos] + node[sizeName] / 2).toFixed(5)) !== parentCenter) {
          node[pos] = parentCenter - node[sizeName] / 2
          if (node.parent) {
            _c(node.parent, direction)
          }
        }
      } else {
        if (Number((node[pos] + node[sizeName]).toFixed(5)) !== parentCenter) {
          node[pos] = parentCenter - node[sizeName]
          if (node.parent) {
            _c(node.parent, direction)
          }
        }
      }
      for (let i = 0; i < children.length; i++) {
        _c(children[i], direction)
      }
    }
  }
  _c(node, direction)

  const offsetX = node.x
  const offsetY = node.y

  // 当非独立主题结构为思维导图的时候，左右两颗树的根节点位置需要统一调整，同时递归调整子节点
  function _t (node, ox, oy) {
    node.x -= ox
    node.y -= oy
    if (hasChild(node)) {
      for (let i = 0; i < node.children.length; i++) {
        _t(node.children[i], ox, oy)
      }
    }
  }

  const independentRootMoveX = independentRootX - offsetX
  const independentRootMoveY = independentRootY - offsetY

  // 独立主题的根节点坐标改为和鼠标拖动的位置一致，同时改变该主题下的所有子主题坐标
  function _x (node) {
    node.x += independentRootMoveX
    node.y += independentRootMoveY
    if (hasChild(node)) {
      for (let i = 0; i < node.children.length; i++) {
        _x(node.children[i])
      }
    }
  }

  /**
   * 结构为思维导图的时候并且第二级主题个数小于等于2的时候重新更新Y坐标
   */
  function _cc () {
    const depth1Children = node.children
    if ([1, 2].includes(depth1Children?.length)) {
      const child0 = depth1Children[0]
      const child1 = depth1Children[1]
      const shape = child0.style.shape
      const between = Math.abs(node.y + node.height / 2 - child0?.y - child0?.height / (['underline', 'doubleUnderline'].includes(shape) ? 1 : 2))
      if (between < 60) {
        const oy = (child0.y + child0.height / (['underline', 'doubleUnderline'].includes(shape) ? 1 : 2)) - (node.y + node.height / 2 - 60)
        child0 && _t(child0, 0, oy)
        child1 && _t(child1, 0, -oy)
      }
    }
  }

  if (!node.data.pos) {
    _t(node, offsetX, offsetY)
    if (skeletonTheme.structure === 'org.xmind.ui.map.clockwise') {
      _cc()
    }
  } else {
    _x(node)
  }
}

/**
 * 树形结构图重新设置根节点的x/y坐标
 * @param {*} nodes
 */
export function treeResetRootNodePos (nodes) {
  const root = nodes.find(o => !o.parent)
  const minY = Math.min(...nodes.map(o => o.y))
  root.y = minY - 50 - root.height
  const offsetX = root.x
  const offsetY = root.y
  for (let i = 0; i < nodes.length; i++) {
    nodes[i].x -= offsetX
    nodes[i].y -= offsetY
  }
}

/**
 * 所有独立主题数据坐标计算
 * @param {*} independents
 * @param {*} skeletonTheme
 * @returns
 */
function generateViceXmindNodeEdges (independents, skeletonTheme) {
  const [viceNodes, viceLinks] = [[], []]
  for (let i = 0; i < independents.length; i++) {
    const viceLayoutdata = dataTreeLayoutPackage(independents[i], 'right', skeletonTheme)
    const { nodes, links } = viceLayoutdata
    if (skeletonTheme.structure === 'org.xmind.ui.brace.right') {
      for (let i = 0; i < links.length; i++) {
        const edge = links[i]
        const parent = edge.target.parent
        const idx = parent.children.findIndex(o => o.data._id === edge.target.data._id)
        if (idx !== 0 && idx !== parent.children.length - 1) {
          links.splice(i, 1)
          i--
        }
      }
    }
    viceNodes.push(...nodes)
    viceLinks.push(...links)
  }
  return {
    viceNodes,
    viceLinks
  }
}

export function generateNodeLinks (root, skeletonTheme, aligning, compact) {
  try {
    const {
      backupTopics,
      independentTopics
    } = backupRootDeleteChild(root)

    const { leftRoot, rightRoot } = splitLeftRightRoot(backupTopics, skeletonTheme.structure)
    const packageLayoutdataLeft = dataTreeLayoutPackage(leftRoot, 'left', skeletonTheme, aligning, compact)
    const packageLayoutdataRight = dataTreeLayoutPackage(rightRoot, skeletonTheme.structure === 'org.xmind.ui.org-chart.down' ? 'bottom' : 'right', skeletonTheme, aligning, compact)
    const leftNodes = packageLayoutdataLeft.nodes.slice(1)
    const leftLinks = packageLayoutdataLeft.links
    const rightNodes = packageLayoutdataRight.nodes
    const rightLinks = packageLayoutdataRight.links
    const mainNodes = [...rightNodes, ...leftNodes]
    const mainEdges = [...rightLinks, ...leftLinks]
    if (skeletonTheme.structure === 'org.xmind.ui.brace.right') {
      for (let i = 0; i < mainEdges.length; i++) {
        const edge = mainEdges[i]
        const parent = edge.target.parent
        const idx = parent.children.findIndex(o => o.data._id === edge.target.data._id)
        if (idx !== 0 && idx !== parent.children.length - 1) {
          mainEdges.splice(i, 1)
          i--
        }
      }
    }

    // 独立主题数据信息
    const { viceNodes, viceLinks } = generateViceXmindNodeEdges(independentTopics, skeletonTheme)

    const relationNodes = [...mainNodes, ...viceNodes].filter(node => node.data.relations?.length)
    relationNodes.forEach(node => {
      node.data.relations.forEach(relation => {
        relation.style = {
          ...skeletonTheme.relationStyle,
          ...relation.style
        }
      })
    })

    return {
      nodes: [...mainNodes, ...viceNodes],
      edges: [...mainEdges, ...viceLinks],
      relationNodes
    }
  } catch (error) {
    throw new Error(error)
  }
}
